/***************************************************************************
 *   oboe.core.SigSlot							 *
 *   Copyright (C) 2011 by Felipe Manga					*
 *   lufeboma@gmail.com							*
 *									 *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the	   *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to	*
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:						 *
 *									 *
 *   The above copyright notice and this permission notice shall be	*
 *   included in all copies or substantial portions of the Software.	   *
 *									 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF	*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.					   *
 ***************************************************************************/

package oboe.core;

import flash.utils.Dictionary;

class SigSlot extends SigSlotCollection
{
private var sigslotParent:SigSlot;
private static var nextSigSlotId:Int = 1;
private var id:Int;
private var pipes:TypedDictionary;
private var signals:Object;


public function new()
{
	this.id = nextSigSlotId++;
	super();
	this.signals = SigSlotCollection.globalSigIndex[ Object(this).constructor ];
}

public function slot__getSigSlotInfo( children:Object, pipes:Object, slots:Object, signals:Object ):Void
{
	var c:TypedDictionary = this._getSigSlotChildren();
	var s:TypedDictionary = getSlots();
	
	for( child in c )
	{
	var type:String = Object(child).constructor.toString();
	if( Reflect.field(children, type) ) )
	{
		children[ type ] = [];
	}
	children[ type ].push( child );
	}
	
	for( var pipe:String in this.pipes )
	{
	pipes[ pipe ] = pipe;
	}
	
	for( var signal:String in this.signals )
	{
	signals[ signal ] = signal;
	}
	
	for( var slot:String in s )
	{
	if( slot.charAt(0) != "_" )
	{
		slots[slot] = slot;
	}
	}
}

public final function slot__onInitPipes( pipes:Dictionary ):Void
{
	if( this.pipes ) return;
	this.pipes = new Dictionary();
	for each( var pipe:String in pipes )
	{
	this.pipes[ pipe ] = this[ "pipe_" + pipe ]; 
	}
}

private function getSigSlotId():Int
{
	return this.id;
}

private function getSigSlotParent():SigSlot
{
	return this.sigslotParent;
}

protected override function addChild( childC:SigSlotCollection ):Void
{
	var child:SigSlot = SigSlot(childC);
	super.addChild( childC );
	
	if( !child || child == this || child.sigslotParent == this ) return;

	if( child.sigslotParent )
	{
	child.sigslotParent.removeChild( child );
	}
		
	child.sigslotParent = this;
	
	child.message("onAddToParent");
}

protected override function removeChild( child:SigSlotCollection ):Void
{
	super.removeChild( child );
	if( !child || !(child is SigSlot) || (SigSlot(child).sigslotParent != this) ) return;
	
	child.message("onRemoveFromParent");
	
	SigSlot(child).sigslotParent = null;
}

private function addPipe( name:String, func:Function ):Void
{
	if( !this.pipes ) this.pipes = new Dictionary();
	this.pipes[ name ] = func;
}

private static inline var SIG_CHILDREN:Int = 1;
private static inline var SIG_PARENT:Int = 2;
private static inline var SIG_RECURSIVE:Int = 5;

private function _emitSignal( name:String, sigtype:Int, arguments:Array ):Void
{
	var slotName:String = "slot_" + name;
	
	if( sigtype&SIG_PARENT )
	{
	if( this.sigslotParent && this.pipes && name in this.pipes )
	{
		this.pipes[name].apply( this, arguments );
		this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
	}
	else
	{
		this.messageEx( name, arguments );
	}
	}
	else
	{
	if( isNaN(Number(Oboe.sigMiss[ name ])) ) Oboe.sigMiss[ name ] = 0;
	Oboe.sigMiss[ name ]++;
	}
	
	if( sigtype&SIG_CHILDREN )
	{
	if( (sigtype&SIG_RECURSIVE) == SIG_RECURSIVE )
	{
		var children:Dictionary = this._getSigSlotChildren();
		for each( var child:SigSlot in children )
		{
		child._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments );
		}
	}
	else 
	{
		super.signalDownEx( name, arguments )
	}
	}
}

private function selfDestruct():Void
{
	if( !this.sigslotParent ) return;
	this.sigslotParent.removeChild( this );
}

public override function message( name:String, ... arguments ):Void
{
	super.messageEx( name, arguments );
	if( this.sigslotParent && this.pipes && name in this.pipes  )
	{
	this.pipes[name].apply( this, arguments );
	this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
	}
}

protected override function signalDown(name:String, ...arguments):Void
{
	signals[ name ] = name;
	super.signalDownEx( name, arguments );
}

protected override function signalDownEx(name:String, arguments:Array):Void
{
	signals[ name ] = name;
	super.signalDownEx( name, arguments );
}

private function signalUp( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	if( this.sigslotParent )
	{
	this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
	}
}

private function shortSignal( name:String, ... arguments ):Void
{		
	signals[ name ] = name;
	if( this.sigslotParent )
	{
	this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_CHILDREN, arguments ); 
	this.signalDownEx( name, arguments );
	}
	else
	{
	this.messageEx( name, arguments );
	this.signalDownEx( name, arguments );
	}
}

private function signal( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	if( this.sigslotParent )
	{
	this.sigslotParent._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments ); 
	}
	else
	{
	this._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments );
	}
}

private function broadcastSignal( name:String, ...arguments ):Void
{
	signals[ name ] = name;
	var root:SigSlot = this;
	while( root.sigslotParent )
	{
	root = root.sigslotParent;
	}
	
	root._emitSignal( name, SIG_PARENT+SIG_RECURSIVE, arguments );
}





private function _signalUp( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	arguments.unshift( name );
	this.signalUp.apply( this, arguments );
}

private function _signalDown( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	arguments.unshift( name );
	this.signalDown.apply( this, arguments );
}

private function _shortSignal( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	arguments.unshift( name );
	this.shortSignal.apply( this, arguments );
}

private function _signal( name:String, ... arguments ):Void
{
	signals[ name ] = name;
	arguments.unshift( name );
	this.signal.apply( this, arguments );
}

private function _broadcastSignal( name:String, ...arguments ):Void
{
	signals[ name ] = name;
	arguments.unshift( name );
	this.broadcastSignal.apply( this, arguments );
}

}